import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib import rcParams
import matplotlib.font_manager as fm
from sklearn.preprocessing import StandardScaler

# 步骤 1：配置Matplotlib中文显示
# 这里指定SimHei字体路径，注意不同操作系统路径会有所不同，此为Windows示例路径
font_path = 'C:/Windows/Fonts/simhei.ttf'
# 利用字体路径创建字体属性对象，用于后续设置字体
prop = fm.FontProperties(fname=font_path)
# 将Matplotlib全局字体设置为SimHei，确保中文正常显示
rcParams['font.family'] = prop.get_name()
# 解决负号在图中显示异常的问题，使其正常显示
rcParams['axes.unicode_minus'] = False

# 步骤 2：加载数据集
# 从UCI机器学习数据库获取葡萄酒数据集的URL
url = "https://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.data"
# 定义数据集列名，包含类别以及各类化学成分指标
columns = ['Class', 'Alcohol', 'Malic_acid', 'Ash', 'Alcalinity_of_ash', 'Magnesium',
           'Total_phenols', 'Flavanoids', 'Nonflavanoid_phenols', 'Proanthocyanins',
           'Color_intensity', 'Hue', 'OD280/OD315', 'Proline']
# 使用pandas读取CSV格式数据，无标题行，按定义列名赋值
data = pd.read_csv(url, header=None, names=columns)

# 步骤 3：数据预处理 - 筛选特定类别数据
# 筛选出类别为1和2的数据子集，便于后续针对性分析
filtered_data = data[data['Class'].isin([1, 2])]
# 提取特征数据，即除类别列外的所有列，作为后续模型输入
X = filtered_data.iloc[:, 1:]

# 步骤 4：特征标准化
# 创建标准化器对象，基于数据的均值和标准差进行标准化
scaler = StandardScaler()
# 对特征数据进行拟合与转换，使其均值为0，方差为1，提升模型收敛速度与性能
X_scaled = scaler.fit_transform(X)


# 步骤 5：K-Means聚类算法实现
def kmeans(X, k=2, max_iters=100):
    """
    K-Means聚类算法核心函数。

    参数:
    X (numpy.ndarray): 输入的特征数据，形状为 (样本数, 特征数)。
    k (int, 可选): 聚类的类别数，默认为2。
    max_iters (int, 可选): 最大迭代次数，防止算法无限循环，默认为100。

    返回:
    centroids (numpy.ndarray): 最终的聚类中心，形状为 (k, 特征数)。
    labels (numpy.ndarray): 每个样本所属的聚类标签，形状为 (样本数,)。
    """
    # 随机从数据集中选取k个样本作为初始聚类中心
    centroids = X[np.random.choice(X.shape[0], k, replace=False)]
    prev_centroids = np.zeros_like(centroids)
    labels = np.zeros(X.shape[0])

    # 迭代优化聚类中心与样本标签
    for _ in range(max_iters):
        # 步骤 6：样本点归类
        for i in range(X.shape[0]):
            # 计算样本点到各个聚类中心的欧氏距离
            distances = np.linalg.norm(X[i] - centroids, axis=1)
            # 将样本点分配到最近的聚类中心，记录其标签
            labels[i] = np.argmin(distances)

        # 步骤 7：更新聚类中心
        for i in range(k):
            # 根据样本标签，重新计算每个聚类的中心，即属于该聚类的样本均值
            centroids[i] = X[labels == i].mean(axis=0)

        # 检查聚类中心是否收敛，若收敛则提前结束迭代
        if np.all(centroids == prev_centroids):
            break
        prev_centroids = centroids.copy()

    return centroids, labels


# 步骤 6：执行K-Means聚类
centroids, labels = kmeans(X_scaled, k=2)

# 步骤 7：可视化聚类结果
plt.figure(figsize=(8, 6))

# 绘制样本点，根据标签区分颜色，展示聚类分布
plt.scatter(X_scaled[:, 0], X_scaled[:, 1], c=labels, cmap='viridis', edgecolor='k', s=50)

# 绘制聚类中心，用红色X标记，突出显示
plt.scatter(centroids[:, 0], centroids[:, 1], c='red', marker='X', s=200, label='聚类中心')

# 添加图表标题与坐标轴标签，清晰说明图表内容
plt.title('K-Means 聚类结果')
plt.xlabel('酒精含量 (Alcohol)')
plt.ylabel('苹果酸含量 (Malic acid)')

# 显示图例，辅助解读图表元素
plt.legend()

# 展示绘制好的图表
plt.show()